import json
import string

from django.db import transaction
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator

from .models import Table, TableFields, DataRecord
from django.conf import settings
from apps.base.serializers import BaseListRetrieveSerializer, BaseCreateSerializer, BaseUpdateSerializer


class TableWriteSerializer(BaseCreateSerializer, BaseUpdateSerializer):
    class Meta:
        model = Table
        fields = ['name', 'descriptions', 'created_by', 'modified_by']


class TableFieldsInfoSerializer(serializers.ModelSerializer):
    extra_conditions = serializers.SerializerMethodField()
    field_type_zh = serializers.CharField(source='get_field_type_display')

    @staticmethod
    def get_extra_conditions(attrs):
        return json.loads(attrs.extra_conditions) if attrs.extra_conditions else None

    class Meta:
        model = TableFields
        fields = ['field_name', 'field_type_zh', 'field_tag', 'field_type', 'extra_conditions', 'is_blank', 'is_search']


class TableRetrieveSerializer(BaseListRetrieveSerializer):
    fields_info = TableFieldsInfoSerializer(source='table_fields_table', many=True)

    class Meta:
        model = Table
        fields = ['id', 'name', 'descriptions', 'created_by', 'modified_at', 'fields_info']


class TableListSerializer(BaseListRetrieveSerializer):
    fields_count = serializers.SerializerMethodField()

    @staticmethod
    def get_fields_count(attrs):
        return attrs.table_fields_table.count()

    class Meta:
        model = Table
        fields = ['id', 'name', 'descriptions', 'created_by', 'created_at', 'fields_count']


class TableFieldsReadSerializer(BaseListRetrieveSerializer):
    extra_conditions = serializers.SerializerMethodField()
    table = serializers.SerializerMethodField()

    @staticmethod
    def get_table(attrs):
        return {'id': attrs.table.id, 'name': attrs.table.name}

    @staticmethod
    def get_extra_conditions(attrs):
        return json.loads(attrs.extra_conditions) if attrs.extra_conditions else None

    class Meta:
        model = TableFields
        fields = '__all__'


class TableFieldsBaseSerializer(serializers.ModelSerializer):
    extra_conditions = serializers.JSONField(allow_null=True, label='限制条件', error_messages={'invalid': '必须为JSON格式'},
                                             help_text='条件传参格式为json类型，可空即 null 例: {"max_length": 128,'
                                                       ' "min_length": 64} 目前支持属性有最大、小值，最大小长度，默认值')

    @staticmethod
    def validate(attrs):
        if attrs['extra_conditions']:
            if attrs['extra_conditions'].keys() - settings.FIELD_PROPERTIES.get(attrs['field_type'], []):
                raise serializers.ValidationError('当前字段没有该条件属性,请核对')
            for i in attrs['extra_conditions'].keys():
                if i in ['max_length', 'min_length']:
                    if type(attrs['extra_conditions'][i]) != int:
                        raise serializers.ValidationError('max_length/min_length请传整数。')
                    if i == 'max_length' and attrs['extra_conditions'][i] < 1:
                        raise serializers.ValidationError('最大长度值不能小于1。')
                    if i == 'min_length' and attrs['extra_conditions'][i] < 0:
                        raise serializers.ValidationError('最小长度值不能小于0')
                if i in ['max_value', 'min_value'] and type(attrs['extra_conditions'][i]) not in [int, float]:
                    raise serializers.ValidationError('设置最大、最小值时请务必填写整数或者小数。')
                if i == 'default_value' and type(attrs['extra_conditions']['default_value']) != eval(
                        attrs['field_type']):
                    raise serializers.ValidationError('默认值类型与字段类型不匹配。')
            if len({'max_length', 'min_length'} - attrs['extra_conditions'].keys()) == 0:
                if attrs['extra_conditions']['max_length'] < attrs['extra_conditions']['min_length']:
                    raise serializers.ValidationError('设置条件请确保最大长度大于最小长度。')
            if len({'max_value', 'min_value'} - attrs['extra_conditions'].keys()) == 0:
                if attrs['extra_conditions']['max_value'] < attrs['extra_conditions']['min_value']:
                    raise serializers.ValidationError('设置条件请确保最大值大于最小值。')
            attrs['extra_conditions'] = json.dumps(attrs['extra_conditions'])
        else:
            attrs['extra_conditions'] = ''
        return attrs

    class Meta:
        model = TableFields
        fields = '__all__'


class TableFieldsCreateSerializer(TableFieldsBaseSerializer):
    created_by = serializers.HiddenField(default=serializers.CurrentUserDefault())

    @staticmethod
    def validate_field_tag(value):
        if value[0] not in string.ascii_letters:
            raise serializers.ValidationError('必须以字母开头')
        elif not set(value).issubset(set(string.ascii_letters + string.digits + '_')):
            raise serializers.ValidationError('只能包含数字字母下划线')
        return value

    class Meta:
        model = TableFields
        fields = ['field_tag', 'field_name', 'field_type', 'descriptions', 'table', 'is_blank', 'is_search',
                  'created_by', 'extra_conditions', 'order']
        validators = [
            UniqueTogetherValidator(
                queryset=TableFields.objects.all(),
                fields=('field_tag', 'table'),
                message='该字段已存在该表，请修改后重新提交'
            )
        ]


class TableFieldsUpdateSerializer(TableFieldsBaseSerializer):
    modified_by = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = TableFields
        fields = ['field_name', 'field_type', 'descriptions', 'is_blank', 'is_search', 'modified_by',
                  'extra_conditions', 'order']

    @transaction.atomic
    def update(self, instance, validated_data):
        field_type = validated_data.pop('field_type', None)
        instance = super().update(instance, validated_data)
        return instance


class DataRecordDynamicSerializer(serializers.Serializer):

    def __init__(self, *args, **kwargs):
        self.dynamic_fields = kwargs['fields']
        del kwargs['fields']
        super().__init__(*args, **kwargs)

    def get_extra_fields(self):
        serializer_field_mapping = {
            'str': serializers.CharField,
            'int': serializers.IntegerField,
            'float': serializers.FloatField,
            'date': serializers.DateField,
            'datetime': serializers.DateTimeField,
            'email': serializers.EmailField,
            'bool': serializers.BooleanField,
            'uuid': serializers.UUIDField,
            'img': serializers.ImageField,
            'file': serializers.FileField
        }
        fields = {}
        for (name, data_type), arg in self.dynamic_fields.items():
            fields[name] = serializer_field_mapping[data_type](**arg)
        return fields

    def get_extra_fields_name(self):
        return self.get_extra_fields().keys()

    def get_fields(self):
        fields = super().get_fields()
        fields.update(**self.get_extra_fields())
        return fields


class DataRecordCreateSerializer(DataRecordDynamicSerializer):

    def validate(self, attrs):
        table_id = self.context['request'].query_params.get('table')
        table = Table.objects.filter(id=table_id).first()
        table_fields = table.table_fields_table.all()
        table_fields_tag = table.table_fields_table.values_list('field_tag', flat=True)
        not_null_fields = attrs.keys()
        null_fields = table_fields_tag - not_null_fields
        for x in table_fields:
            if x.field_tag in null_fields:
                continue
            if attrs[x.field_tag] is None:
                del attrs[x.field_tag]
                continue
            if x.field_type in ['uuid', 'int', 'float']:
                attrs[x.field_tag] = str(attrs[x.field_tag])
            if x.field_type == 'bool':
                attrs[x.field_tag] = 'T' if attrs[x.field_tag] else 'F'
            if x.field_type in ['img', 'file']:
                path = '{0}/files/{1}'.format(settings.MEDIA_ROOT, attrs[x.field_tag].name)
                with open(path, 'wb') as f:
                    f.write(attrs[x.field_tag].file.read())
                attrs[x.field_tag] = '/media/files/{}'.format(attrs[x.field_tag].name)
        return attrs

    class Meta:
        model = DataRecord
        exclude = ['id', 'modified_by', 'modified_at', 'table', 'field_value', 'field_tag', 'row_num', 'created_at',
                   'created_by']


class DataRecordUpdateSerializer(DataRecordCreateSerializer):
    row_num = serializers.IntegerField(write_only=True, required=True, label='行数', help_text='填写想要更新的记录行')

    class Meta:
        model = DataRecord
        exclude = ['id', 'modified_by', 'modified_at', 'table', 'field_value', 'field_tag', 'created_at', 'created_by']


class DataRecordDestroySerializer(serializers.Serializer):
    row_num = serializers.IntegerField(write_only=True, required=True, label='行数', help_text='填写想要删除的记录行')


class DataRecordReadSerializer(BaseListRetrieveSerializer):
    class Meta:
        model = DataRecord
        fields = ['row_num', 'field_tag', 'field_value']
